package domain;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import pagemaker.ExternalProgramException;

public class PDFLibrary {
    private int                       nbDocs, groupSize, currentGroupElement;
    private String                    libraryFilename;
    private ArrayList<PDFdoc>         ssLibrary = new ArrayList<PDFdoc>();
    private ArrayList<PDFdoc>         selection = new ArrayList<PDFdoc>(ssLibrary);

    /**
     * Note the private constructor. The class must implement the Singleton
     * Pattern.
     * 
     * @param filename
     */
    private static PDFLibrary instance  = null;

    private PDFLibrary(String filename) {
        nbDocs = 0;
        this.libraryFilename = filename;
    }

    /**
     * Given a filename, returns an instance of PDFLibrary. The filename is used
     * to save library's content.
     * 
     * @param filename
     * @return
     */
    public static PDFLibrary makePDFLibrary(String filename) {
        if (instance == null)
            instance = new PDFLibrary(filename);
        return instance;
    }

    /**
     * Saves the library to the disc.
     */
    public void saveLibrary() {
        try {
            FileWriter pdfLibrary = new FileWriter(libraryFilename);
            for (PDFdoc pdf : ssLibrary)
                pdfLibrary.write(pdf.toString());
            pdfLibrary.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * Loads a library from the disc.
     */
    public void loadLibrary() {
        String line;
        String[] values;
        PDFdoc pdf;
        try {
            BufferedReader pdfLibrary = new BufferedReader(new FileReader(
                    libraryFilename));
            while ((line = pdfLibrary.readLine()) != null) {
                values = line.split(";");
                File file = new File(values[0]);
                try {
                    pdf = PDFdoc.makePDFdoc(file);
                    pdf.addTags(values[1], ",");
                    pdf.setTitle(values[2]);
                    addDoc(pdf);
                } catch (ExternalProgramException e) {
                    e.printStackTrace();
                }
            }
            pdfLibrary.close();
        } catch (IOException e) {
            System.err.println("Not file to load!!!!");
        }
    }

    /**
     * Adds a document to the library.
     * 
     * @param doc
     */
    public void addDoc(PDFdoc doc) {
        doc.setDocumentNumber(nbDocs + 1);
        ssLibrary.add(doc);
        resetSelection();
        nbDocs = ssLibrary.size();
    }

    /**
     * Removes a document from the library.
     * 
     * @param doc
     */
    public void removeDoc(PDFdoc doc) {
        ssLibrary.remove(doc);
        resetSelection();
        nbDocs = ssLibrary.size();
    }

    /**
     * Returns the number of documents present in the library.
     * 
     * @return
     */
    public int getNumberOfDocuments() {
        return nbDocs;
    }

    /**
     * Given a set of tags, select the documents that have at least one of these
     * tags. The result is a possibly large set of documents referred as the
     * "selection".
     * 
     * @param tags
     */
    public void selectDocsWithOneOfTheseTags(ArrayList<String> currentTags) {
        boolean found;
        if (!currentTags.isEmpty()) {
            Set<PDFdoc> selectionCopy = new TreeSet<PDFdoc>(selection);
            for (PDFdoc doc : selectionCopy) {
                found = false;
                for (String tag : currentTags)
                    found = doc.tags.contains(tag) ? found || true
                            : found || false;
                if (!found) selection.remove(doc);
            }
        } else
            resetSelection();
    }

    /**
     * Given a set of tags, select the documents that have all the tags. The
     * result is a possibly large set of documents referred as the "selection".
     * 
     * @param tags
     */
    public void selectDocsWithAllTheseTags(ArrayList<String> currentTags) {
        if (!currentTags.isEmpty()) {
            Set<PDFdoc> selectionCopy = new TreeSet<PDFdoc>(selection);
            for (PDFdoc doc : selectionCopy)
                if (!doc.tags.containsAll(currentTags)) selection.remove(doc);
        } else
            resetSelection();
    }

    /**
     * The selection is divided into subsets of documents called "groups". This
     * method returns the first group of the selection.
     * 
     * @return
     */
    public List<PDFdoc> getFirstGroup() {
        this.currentGroupElement = 0;
        List<PDFdoc> list = new ArrayList<PDFdoc>();
        for (int i = 0; i < Math.min(this.groupSize, selection.size()); i++)
            list.add(selection.get(i));
        return list;
    }

    /**
     * Returns the previous group of documents.
     * 
     * @return
     */
    public List<PDFdoc> getPreviousGroup() {
        int start, end;
        List<PDFdoc> list = new ArrayList<PDFdoc>();

        if (hasPreviousPage()) {
            start = (currentGroupElement - 1) * groupSize;
            end = start + groupSize;
            currentGroupElement--;
        } else {
            start = 0;
            end = start + groupSize;
        }
        for (int i = start; i < end; i++)
            list.add(selection.get(i));

        return list;
    }

    /**
     * Returns the next group of documents.
     * 
     * @return
     */
    public List<PDFdoc> getNextGroup() {
        int start;
        int end;

        List<PDFdoc> list = new ArrayList<PDFdoc>();
        if (hasNextPage()) {
            start = (currentGroupElement + 1) * groupSize;
            end = Math.min(start + groupSize, selection.size());
            currentGroupElement++;
        } else {
            start = currentGroupElement * groupSize;
            end = selection.size();
        }
        for (int i = start; i < end; i++)
            list.add(selection.get(i));

        return list;
    }

    /**
     * Changes the size (number of elements) that from a group of documents.
     * 
     * @param size
     */
    public void setGroupSize(int size) {
        this.groupSize = size;
    }

    /**
     * Returns the number of the current group of documents.
     * 
     * @return
     */
    public int getCurrentGroupNumber() {
        return currentGroupElement;
    }

    /**
     * Returns the number of groups in the selection
     * 
     * @return
     */
    public int nbGroupsInSelection() {
        if (selection.size() % groupSize == 0) return selection.size()
                / groupSize;
        else
            return (selection.size() / groupSize) + 1;
    }

    /**
     * Returns the number of elements in the selection.
     * 
     * @return
     */
    public int selectionSize() {
        return selection.size();
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        for (PDFdoc pdf : ssLibrary)
            sb.append(pdf.toString() + "\n");
        return sb.toString();
    }

    private boolean hasPreviousPage() {
        return this.currentGroupElement + 1 > 1;
    }

    private boolean hasNextPage() {
        return (this.currentGroupElement + 1) < nbGroupsInSelection();
    }

    private void resetSelection() {
        selection = new ArrayList<PDFdoc>(ssLibrary);
    }
}